Skip to content

Conversation

@yume-chan
Copy link
Owner

@yume-chan yume-chan commented Jan 6, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Improved video codec configuration handling and validation to prevent decoding errors.
    • Enhanced keyframe sequencing for more reliable video decoding across different devices.
    • Better error detection for misconfigured decoders.
  • Performance

    • Optimized latency management by constraining frame delays on slower devices.
    • Improved decoder recovery from frame backlog.

✏️ Tip: You can customize this high-level summary in your review settings.

@diffray diffray bot added the diffray-review-started diffray review status: started label Jan 6, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 6, 2026

📝 Walkthrough

Walkthrough

This PR refactors the WebCodecs decoder configuration and state management across AV1, H264, H265, and H26x codecs. The configure() method return type changes from void to VideoDecoderConfig, shifting responsibility from internal decoder configuration to returning configuration objects. AV1 adds configuration state tracking with deferred setup until keyframes. H26x implements a two-step configuration flow with explicit keyframe sequencing and error validation.

Changes

Cohort / File(s) Summary
Configure return type refactoring
libraries/scrcpy-decoder-webcodecs/src/video/codec/h264.ts, libraries/scrcpy-decoder-webcodecs/src/video/codec/h265.ts
Changed configure() return type from void to VideoDecoderConfig. Removed internal #decoder field storage; methods now return configuration objects instead of invoking internal decoder configuration.
AV1 configuration state management
libraries/scrcpy-decoder-webcodecs/src/video/codec/av1.ts
Added #config and #configured state tracking. Replaced #configure() with #parseConfig() to defer actual decoder configuration until keyframe arrival. Enhanced keyframe handling with conditional decoder reset and re-configuration. Updated encoding to use WebCodecs "key"/"delta" type mapping.
H26x configuration and keyframe handling
libraries/scrcpy-decoder-webcodecs/src/video/codec/h26x.ts
Implemented two-step configuration: stores merged config on configuration packets, defers decoder setup until keyframe. Added #configureAndDecodeFirstKeyframe() helper and #configured flag. Added explicit error validation for configuration order and keyframe sequencing. Includes recovery logic for slow decoders with decoder reset on subsequent keyframes.

Sequence Diagram(s)

sequenceDiagram
    participant App as Application
    participant H26x as H26xDecoder
    participant VideoDecoder as WebCodecs VideoDecoder

    App->>H26x: decode(config packet)
    H26x->>H26x: Store merged config + raw data<br/>Set configured = false
    
    App->>H26x: decode(keyframe packet)
    alt First frame after config
        H26x->>H26x: `#configureAndDecodeFirstKeyframe`()
        H26x->>VideoDecoder: configure(mergedConfig)
        H26x->>VideoDecoder: decode(EncodedVideoChunk)
        H26x->>H26x: Set configured = true
    else Subsequent keyframe
        alt decodeQueueSize exists
            H26x->>VideoDecoder: reset()
            H26x->>VideoDecoder: configure(mergedConfig)
            H26x->>VideoDecoder: decode(EncodedVideoChunk)
        end
    end
    
    App->>H26x: decode(delta frame packet)
    alt configured
        H26x->>VideoDecoder: decode(EncodedVideoChunk type=delta)
    else not configured
        H26x->>App: throw "Decoder not configured"
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • PR #790: Refactors codec parsing and codec-string generation into a new @yume-chan/media-codec package, with WebCodecs decoders updated to consume the new parse/toCodecString APIs and adjusted configuration flows.
  • PR #822: Modifies decoder class configuration paths and constructor behavior for H264, H265, and AV1 codecs with similar configuration flow patterns.
  • PR #782: Updates WebCodecs decoder codepaths for AV1/H26x, modifying configuration, keyframe handling, and decoding chunk management.
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(webcodecs): reset the decoder on every keyframe' accurately summarizes the main change across the affected files, specifically the enhanced keyframe handling with decoder resets to improve recovery and latency.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Owner Author

@yume-chan yume-chan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai review

@diffray diffray bot added diffray-review-failed diffray review status: failed and removed diffray-review-started diffray review status: started labels Jan 6, 2026
@diffray diffray bot added diffray-review-started diffray review status: started diffray-review-failed diffray review status: failed and removed diffray-review-failed diffray review status: failed diffray-review-started diffray review status: started labels Jan 6, 2026
@yume-chan yume-chan removed the diffray-review-failed diffray review status: failed label Jan 6, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @libraries/scrcpy-decoder-webcodecs/src/video/codec/av1.ts:
- Around line 100-123: The decoder can attempt to decode delta frames before
being configured and will throw InvalidStateError; update the logic in the
packet handling flow (after this.#parseConfig(packet.data)) to check that if
packet.keyframe is false and either this.#configured is false or this.#config is
missing, you should throw a clear Error (e.g., "Decoder not configured for delta
frame") or otherwise prevent calling this.#decoder.decode(); reference
this.#parseConfig, this.#config, packet.keyframe, this.#configured and
this.#decoder.decode to locate the spot to add the guard so delta frames never
reach decode() when the decoder is unconfigured.
🧹 Nitpick comments (1)
libraries/scrcpy-decoder-webcodecs/src/video/codec/h264.ts (1)

39-44: Consider adding codedWidth and codedHeight for consistency with H265Decoder.

The H265Decoder includes codedWidth and codedHeight in the returned config with a comment stating "Microsoft Edge requires explicit size to work." If this Edge requirement also applies to H.264 decoding, the dimensions should be included here as well.

Suggested change
         return {
             codec: codec,
+            codedWidth: croppedWidth,
+            codedHeight: croppedHeight,
             hardwareAcceleration:
                 this.#options?.hardwareAcceleration ?? "no-preference",
             optimizeForLatency: true,
         };
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0236f2e and 67a670c.

📒 Files selected for processing (4)
  • libraries/scrcpy-decoder-webcodecs/src/video/codec/av1.ts
  • libraries/scrcpy-decoder-webcodecs/src/video/codec/h264.ts
  • libraries/scrcpy-decoder-webcodecs/src/video/codec/h265.ts
  • libraries/scrcpy-decoder-webcodecs/src/video/codec/h26x.ts
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: yume-chan
Repo: yume-chan/ya-webadb PR: 782
File: libraries/scrcpy-decoder-tinyh264/src/decoder.ts:193-196
Timestamp: 2025-08-24T03:50:43.091Z
Learning: In TinyH264Decoder, configuration failures should propagate the actual error up the client pipeline rather than being recovered locally. The decoder is designed to handle mid-stream configuration switches when the video encoder restarts, and subsequent valid config packets will re-configure correctly regardless of current decoder state.
📚 Learning: 2025-08-24T03:50:43.091Z
Learnt from: yume-chan
Repo: yume-chan/ya-webadb PR: 782
File: libraries/scrcpy-decoder-tinyh264/src/decoder.ts:193-196
Timestamp: 2025-08-24T03:50:43.091Z
Learning: In TinyH264Decoder, configuration failures should propagate the actual error up the client pipeline rather than being recovered locally. The decoder is designed to handle mid-stream configuration switches when the video encoder restarts, and subsequent valid config packets will re-configure correctly regardless of current decoder state.

Applied to files:

  • libraries/scrcpy-decoder-webcodecs/src/video/codec/h264.ts
  • libraries/scrcpy-decoder-webcodecs/src/video/codec/h26x.ts
  • libraries/scrcpy-decoder-webcodecs/src/video/codec/av1.ts
  • libraries/scrcpy-decoder-webcodecs/src/video/codec/h265.ts
📚 Learning: 2025-08-28T05:42:55.846Z
Learnt from: yume-chan
Repo: yume-chan/ya-webadb PR: 790
File: libraries/media-codec/src/h265.ts:23-24
Timestamp: 2025-08-28T05:42:55.846Z
Learning: In HEVC codec strings per ISO/IEC 14496-15:2017 Section E.3, leading zeros may be omitted in hexadecimal encoding for general_profile_compatibility_flags and constraint flags. Zero-padding is not required and the current hexDigits implementation is correct.

Applied to files:

  • libraries/scrcpy-decoder-webcodecs/src/video/codec/h264.ts
  • libraries/scrcpy-decoder-webcodecs/src/video/codec/h265.ts
📚 Learning: 2026-01-12T06:58:19.519Z
Learnt from: yume-chan
Repo: yume-chan/ya-webadb PR: 827
File: libraries/scrcpy-decoder-webcodecs/src/video/utils/video-decoder-stream.ts:105-120
Timestamp: 2026-01-12T06:58:19.519Z
Learning: In TransformStream implementations, calling controller.error() immediately clears the internal [[flushAlgorithm]] and [[cancelAlgorithm]] slots, which prevents the flush() and cancel() callbacks from being invoked. Therefore, if a decoder’s error callback calls controller.error(), the stream’s cancel() callback will not run later. Treat this as a सच rule: do not rely on error() to trigger cleanup through cancel; instead, perform explicit cleanup and, if needed, initiate cancel() or reject the downstream promise yourself. Apply this guidance broadly to all TransformStream-based code paths (not just this file) and ensure error handling paths explicitly manage resource cleanup and stream termination without assuming cancel will be invoked.

Applied to files:

  • libraries/scrcpy-decoder-webcodecs/src/video/codec/h264.ts
  • libraries/scrcpy-decoder-webcodecs/src/video/codec/h26x.ts
  • libraries/scrcpy-decoder-webcodecs/src/video/codec/av1.ts
  • libraries/scrcpy-decoder-webcodecs/src/video/codec/h265.ts
🧬 Code graph analysis (3)
libraries/scrcpy-decoder-webcodecs/src/video/codec/h26x.ts (2)
libraries/scrcpy-decoder-webcodecs/src/video/codec/type.ts (1)
  • CodecDecoder (3-5)
libraries/adb-daemon-webusb/src/device.ts (1)
  • raw (241-243)
libraries/scrcpy-decoder-webcodecs/src/video/codec/av1.ts (1)
libraries/scrcpy/src/codec/nalu.ts (1)
  • Error (214-221)
libraries/scrcpy-decoder-webcodecs/src/video/codec/h265.ts (1)
libraries/scrcpy-decoder-webcodecs/src/video/codec/av1.ts (1)
  • data (25-93)
🔇 Additional comments (8)
libraries/scrcpy-decoder-webcodecs/src/video/codec/h265.ts (1)

22-52: LGTM! Clean refactor to return configuration.

The configure() method now correctly returns a VideoDecoderConfig object instead of configuring the decoder directly. This aligns with the new pattern where the base class H26xDecoder handles decoder configuration and reset logic.

Note: H265Decoder includes codedWidth and codedHeight in the config (for Microsoft Edge compatibility), while H264Decoder in this PR does not. If Edge also requires explicit dimensions for H.264, consider adding them there as well for consistency.

libraries/scrcpy-decoder-webcodecs/src/video/codec/av1.ts (2)

86-93: Every keyframe triggers reconfiguration due to #configured = false in #parseConfig.

Setting #configured = false at line 92 means every call to #parseConfig (which happens on every packet) will mark the decoder as unconfigured. Combined with the logic at lines 119-121, this means every keyframe will call this.#decoder.configure() even when the decode queue is empty.

If this is intentional (per the PR title "reset the decoder on every keyframe"), the current implementation achieves this for keyframes but doesn't actually call reset() when decodeQueueSize is 0—it only reconfigures. However, if the intent is to only reset/reconfigure when the queue is backed up, the #configured = false should be conditional.

Is the intention to reconfigure on every keyframe regardless of queue size, or only when decodeQueueSize > 0? If the latter, the #configured flag should only be reset when the config actually changes:

Possible fix if only reset on config change
         this.#config = {
             codec,
             hardwareAcceleration:
                 this.#options?.hardwareAcceleration ?? "no-preference",
             optimizeForLatency: true,
         };
-        this.#configured = false;
+        // Only mark as unconfigured if this is a new/changed config
+        // Or remove this line if reconfiguration should only happen on queue backup
     }

125-132: LGTM! Clear handling of keyframe type mapping.

The non-null assertion on packet.keyframe! is appropriate given the documented requirement that AV1 streams from Scrcpy 2.0+ always include the keyframe flag. The comment clearly explains this assumption.

libraries/scrcpy-decoder-webcodecs/src/video/codec/h26x.ts (5)

11-12: Good addition of configuration state tracking.

The #config field stores the video decoder config along with the raw configuration data needed for Annex B concatenation. The #configured flag properly tracks whether the decoder has been configured, enabling the reset-on-keyframe pattern.


20-43: LGTM! Correct Annex B handling for H.264/H.265.

The helper correctly implements the WebCodecs AVC codec registration requirement where configuration data must be combined with the first frame data for Annex B format streams. The reference to the W3C spec is helpful.


59-72: LGTM! Effective latency-limiting reset strategy.

The reset logic correctly identifies when the decode queue is backed up and resets on the next keyframe. The comment clearly explains the rationale—limiting maximum latency to one keyframe interval (60 frames by default). This aligns with the PR objective.


80-90: Good backward compatibility for legacy Scrcpy versions.

The handling of packet.keyframe === undefined for Scrcpy <1.23 is a thoughtful addition. The comment explains the behavior, and the fallback to treating the first frame as a keyframe (with VideoDecoder validating) is pragmatic. The explicit error for modern Scrcpy receiving delta frames before keyframes provides clear feedback.


92-99: LGTM! Correct frame type handling.

The ternary packet.keyframe === false ? "delta" : "key" correctly treats undefined as key for backward compatibility with older Scrcpy versions, as documented in the comment.

@yume-chan yume-chan merged commit db2f96c into v2.x Jan 17, 2026
1 check passed
@yume-chan yume-chan deleted the feat/reset-decoder branch January 17, 2026 07:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants